#include "ElementoParseado.h"
#include "Ubicador.h"
#include "lista.h"
#include "vector_dinamico.h"

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

/* *****************************************************************
 *                DEFINICION DE LOS TIPOS DE DATOS
 * *****************************************************************/
 
struct _ElementoParseado {
	char* cadenaParseada;
	int longitud;
	lista_t* ubicaciones;
};

/* ******************************************************************
 *            			PRIMITIVAS AUXILIARES						*
 * *****************************************************************/
					  /* FUNCIONES INTERNAS */

/* Busca el Ubicador correspondiente al número de archivo pasado por
 * parámetro que se encuentra en el elemento parseado. */
lista_iter_t* buscarUbicador_PorArchivo (ElementoParseado_t* elem, int archivo) {
	lista_iter_t* iter = lista_iter_crear (elem->ubicaciones);
	if (!iter) return NULL;
	
	Ubicador_t* ubicacion;
	while ( !(lista_iter_al_final (iter)) ) {
		ubicacion = lista_iter_ver_actual (iter);
		int ubi_archivo = Ubicador_verArchivo (ubicacion);
		if ( ubi_archivo == archivo ) return iter;
		lista_iter_avanzar (iter);
	}
	lista_iter_destruir(iter);
	return NULL;
}


/* Función auxiliar para destruir los elementos de la lista interna
 * de la estructura */
void nodo_ubicador_destruir (void* nodo) {
	Ubicador_destruir ( (Ubicador_t*) nodo );
}

//************

/* *****************************************************************
 *                PRIMITIVAS DEL ELEMENTO PARSEADO
 * *****************************************************************/

/* Crea y devuelve un ElementoParseado. 'cadena' es la palabra o frase
 * parseada y 'largo' su longitud. En caso de error, devuelve NULL. */
ElementoParseado_t* ElementoParseado_crear (char* cadena, int largo) {
	if ( (!cadena) || (largo == 0) ) return NULL;
	
	ElementoParseado_t* elem = malloc (sizeof(ElementoParseado_t));
	if (!elem) return NULL;
	
	elem->cadenaParseada = malloc((largo + 1)*sizeof(char) );
	if (!(elem->cadenaParseada)) {
		free (elem);
		return NULL;
	}
	strcpy(elem->cadenaParseada, cadena );
	elem->longitud = largo;
	elem->ubicaciones = lista_crear ();
	if (!(elem->ubicaciones)) {
		free(elem->cadenaParseada);
		free(elem);
		return NULL;
	}
	return elem;
}

/* Devuelve true si logró agregar la ubicación exitosamente, false en
 * caso contrario */
bool ElementoParseado_agregarUbicacion (ElementoParseado_t* elem, int pos, int archivo) {
	lista_iter_t* iterador = buscarUbicador_PorArchivo (elem, archivo);
	
	if (!iterador) { // si no habia ninguna ubicacion con este archivo
		Ubicador_t* ubicacion = Ubicador_crear (pos, archivo);
		if (!ubicacion) return false;
		return ( lista_insertar_ultimo (elem->ubicaciones ,ubicacion) );
	}
	
	// ya hay palabras con ese archivo, agrego solo el numero nuevo.
	Ubicador_t* ubicacion = lista_iter_ver_actual (iterador);
	lista_iter_destruir(iterador);
	if (!ubicacion) return false;
	return ( Ubicador_agregarPosicion (ubicacion, pos) );
}

/* Devuelve true si logró borrar la ubicación exitosamente, false en
 * caso contrario */
void ElementoParseado_borrarUbicacion (ElementoParseado_t* elem, int pos, int archivo) {
	lista_iter_t* iterador = buscarUbicador_PorArchivo (elem, archivo);
	if (!iterador) return;
	
	Ubicador_t* ubicacion = lista_iter_ver_actual (iterador);
	if (!ubicacion) return;
	
	Ubicador_borrarPosicion (ubicacion, pos);
	lista_iter_destruir (iterador);
}

/* Devuelve una lista con todas las ubicaciones del elemento en formato
 * Ubicador_t.
 * IMPORTANTE: La lista NO debe ser destruida. */
lista_t* ElementoParseado_verUbicaciones (ElementoParseado_t* elem) {
	if (!elem) return NULL;
	return ( elem->ubicaciones );
}

/* Devuelve un vector dinámico con int* correspondientes a los documentos
 * en los que aparece representados en DISTANCIAS */
vector_dinamico_t* ElementoParseado_verArchivos (ElementoParseado_t* elem) {
	if (!elem) return NULL;
	lista_iter_t* iter = lista_iter_crear (elem->ubicaciones);
	if (!iter) return NULL;
	
	vector_dinamico_t* vector = vector_crear ( lista_largo(elem->ubicaciones) , free );
	if (!vector) {
		lista_iter_destruir(iter);
		return NULL;
	}
	Ubicador_t* ubicacion;
	int cant = 0; int ultimoDoc = 0;
	while ( !(lista_iter_al_final (iter)) ) {
		ubicacion = lista_iter_ver_actual (iter);
		int ubi_archivo = Ubicador_verArchivo (ubicacion);
		int* num = malloc (sizeof(int));
		if (!num) {
			vector_destruir (vector);
			lista_iter_destruir(iter);
			return NULL;
		}
		*num = ubi_archivo - ultimoDoc;
		vector_guardar (vector, cant, num);
		lista_iter_avanzar (iter);
		cant++;
		ultimoDoc = ubi_archivo;
	}
	lista_iter_destruir(iter);
	return vector;
}

/* Devuelve la cantidad de ubicaciones del Elemento parseado */
int ElementoParseado_verCantidadUbicaciones (ElementoParseado_t* elem) {
	if (!elem) return -1;
	return ( lista_largo (elem->ubicaciones) );
}

/* Devuelve el nombre de la cadena Parseada o null si elem no existe */
char* ElementoParseado_verCadenaParseada(ElementoParseado_t* elem){
	if (!elem) return NULL;
	return ( elem->cadenaParseada);
}

/* Devuelve la longitud de la cadena Parseada o -1 si elem no existe*/
int ElementoParseado_verLongitud(ElementoParseado_t* elem){
	if (!elem) return -1;
	return ( elem->longitud);
}

/* Destruye el ElementoParseado pasado por parámetro */
void ElementoParseado_destruir (ElementoParseado_t* elem) {
	lista_destruir (elem->ubicaciones, nodo_ubicador_destruir);
	free (elem->cadenaParseada);
	free (elem);
}
